//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Extensibility;

namespace OpenLS.Spreadsheet
{
    public class FullRowRange: FullRange
    {
        public FullRowRange(Row row): this (row, row)
        {
        }
        internal override string 
            LabelForSelection
        {
            get
            {
                return "A" + FirstRow.Label + ":" + Worksheet.Columns[Worksheet.MaxColumnIndex].Label + LastRow.Label;
            }
        }
        internal override IEnumerable<Column> GetExistingColumns()
        {
            return Worksheet.Columns;
        }
        internal override bool Contains(Range range)
        {
            if (range is CellsRange)
            {
                var cr = (CellsRange) range;
                return Contains(cr.TopLeftCell) && Contains(cr.BottomRight);
            }
            if (range is FullRowRange)
            {
                var rr = (FullRowRange) range;
                return ContainsRow(rr.FirstRow) && ContainsRow(rr.LastRow);
            }
            return ContainsCompositeRange(range);
        }
        public FullRowRange(Row row1, Row row2) : base(row1.Worksheet)
        {
            if (row1.Index < row2.Index)
            {
                _firstRow = row1;
                _lastRow = row2;
            }
            else
            {
                _firstRow = row2;
                _lastRow = row1;
            }
            //anchor = row1;

        }
        public override int Row
        {
            get
            {
                return FirstRow.Index;
            }
        }
        public override int Column
        {
            get
            {
                return 0;
            }
        }

        internal override SimpleRange ProjectedSimple(Worksheet worksheet, CellRelativeAddress diff)
        {
            return new FullRowRange(worksheet.Rows[FirstRow.Index + diff.Row], worksheet.Rows[LastRow.Index + diff.Row]);
        }
        internal override IEnumerable<PartialRowOrColumnRange> GetPartialRowOrColumnRanges(RowOrColumn rowOrColumn)
        {
            foreach (Row row in GetDimensions())
                yield return new PartialRowOrColumnRange(Worksheet, new CellAddress(row.Index, 0), 1000, rowOrColumn); ///\\\\
        }

        internal override void SetBorderNoUndo(Border border, BorderType borderType)
        {
            if (borderType == BorderType.All)
            {
                foreach (Row row in this.GetRows())
                {
                    row.SetBorder(borders => borders.Left = border);
                    row.SetBorder(borders => borders.Right = border);
                    row.SetBorder(borders => borders.Top = border);
                    row.SetBorder(borders => borders.Bottom = border);
                }
            }
            if ((borderType & BorderType.TopEdge) != 0)
            {
                this.FirstRow.SetBorder(borders => borders.Top = border);
            }
            if ((borderType & BorderType.BottomEdge) != 0)
            {
                this.LastRow.SetBorder(borders => borders.Bottom = border);
            }
            if ((borderType & BorderType.LeftEdge) != 0)
            {
                foreach (Cell cell in GetLeftCells())
                {
                    cell.SetBorder(border, BorderType.LeftEdge);
                }
            }
            if ((borderType & BorderType.RightEdge) != 0)
            {
                foreach (Cell cell in GetRightCells())
                {
                    cell.SetBorder(border, BorderType.RightEdge);
                }
            }
            if ((borderType & BorderType.InsideVertical) != 0)
            {
                foreach (var row in this.GetRows())
                    row.SetBorder(borders =>
                    {
                        borders.Left = border;
                        borders.Right = border;
                    });
                foreach (var cell in GetNonEmptyCells())
                    cell.SetBorder(border, BorderType.RightEdge | BorderType.LeftEdge);
            }
        }
        internal IEnumerable<Cell> GetLeftCells()
        {
            return Enumerable.Select(this.GetRows(), c => Worksheet[c.Index, 0]);
            ;

        }
        internal IEnumerable<Cell> GetRightCells()
        {
            return Enumerable.Select(this.GetRows(), c => Worksheet[c.Index, Worksheet.MaxColumnIndex]);
        }

        public override Cell TopLeftCell
        {
            get { return Worksheet[FirstRow.Index, 0]; }
        }

        internal override RangeReference ToReference()
        {
            return new FullRowRangeReference(new AnchoredRowReference(FirstRow, true),
                new AnchoredRowReference(LastRow, true));
        }


        private Row _firstRow;
        private Row _lastRow;
        internal Row anchor { get { return FirstRow; } }
        internal Row FirstRow { get { return _firstRow; } 
        private set
        {
            this.Worksheet.Workbook.AddUndoAction(_firstRow, r => FirstRow = r);
            _firstRow = value;
        }}
        internal Row LastRow { get { return _lastRow; }
        private set
        {
            this.Worksheet.Workbook.AddUndoAction(_lastRow, r => LastRow = r);
            _lastRow = value;
        }}

        public override int RowSpan
        {
            get
            {
                return LastRow.Index - FirstRow.Index + 1;
            }   
        }
        internal override IEnumerable<Dimension> GetDimensions()
        {
            for (int i = FirstRow.Index; i <= LastRow.Index; i++)
                yield return Worksheet.Rows[i];
        }


        internal override void OnDeletingRange(RangeChangeEventArgs e)
        {
            Contract.Assert(e != null);
            Contract.Assert(e.Type == RangeChangeType.Deletion);
            if (e.Address.IsFullRow && e.Hint == CellMoveOrientationHint.Vertical)
            {
                if (e.Address.Contains(new CellAddress(FirstRow.Index, 0)))
                {
                    FirstRow= Worksheet.Rows[e.Address.BottomRight.Row + 1];
                    
                }
                if (e.Address.Contains(new CellAddress(LastRow.Index, 0)))
                {
                    LastRow = Worksheet.Rows[Math.Max(e.Address.TopLeft.Row - 1, 0)];
                }
            }
        }

        internal override Dimension FirstDimension
        {
            get { return FirstRow;}
        }

        internal override Dimension LastDimension
        {
            get { return LastRow; }
        }


        internal override IEnumerable<Border> GetBorders(BorderType borderType)
        {
            switch (borderType)
            {
                case BorderType.LeftEdge:
                    return Enumerable.Select(GetLeftCells(), c => c.GetActualBorder(borderType));
                    case BorderType.RightEdge:
                    return Enumerable.Select(GetRightCells(), c => c.GetActualBorder(borderType));
                    case BorderType.TopEdge:
                    {
                        var cellBorders = Enumerable.Select(FirstRow.GetNonEmptyCells(),
                                                            c => c.GetActualBorder(borderType));
                        var columnBorders = Enumerable.Select(this.GetNonEmptyColumns(),
                                                              r =>
                                                              r.Borders != null ? r.Borders.GetBorder(borderType) : null);
                        columnBorders = Enumerable.Distinct(Enumerable.Where(columnBorders, b => b != null));
                        return Enumerable.Concat(cellBorders, columnBorders,
                                                 new Border[]
                                                     {
                                                         FirstRow.Borders != null
                                                             ? FirstRow.Borders.GetBorder(borderType)
                                                             : null
                                                     });
                    }
                case BorderType.BottomEdge:
                        {
                            var cellBorders = Enumerable.Select(LastRow.GetNonEmptyCells(),
                                                                   c => c.GetActualBorder(borderType));
                            var columnBorders = Enumerable.Select(this.GetNonEmptyRows(),
                                                               r =>
                                                               r.Borders != null ? r.Borders.GetBorder(borderType) : null);
                            columnBorders = Enumerable.Distinct(Enumerable.Where(columnBorders, b => b != null));
                            return Enumerable.Concat(cellBorders, columnBorders,
                                                     new Border[]
                                                     {
                                                         LastRow.Borders != null
                                                             ? LastRow.Borders.GetBorder(borderType)
                                                             : null
                                                     });

                        }

                    case BorderType.DiagonalDown:
                    case BorderType.DiagonalUp:
                        {
                            var cellBorders = Enumerable.Select(this.GetNonEmptyCells(),
                                                                  c => c.GetActualBorder(borderType));
                            var rowBorders = Enumerable.Select(this.GetNonEmptyRows(),
                                                               r =>
                                                               r.Borders != null ? r.Borders.GetBorder(borderType) : null);
                            rowBorders = Enumerable.Distinct(Enumerable.Where(rowBorders, b => b != null));
                            var columnBorders = Enumerable.Select(this.GetNonEmptyColumns(),
                                                               r =>
                                                               r.Borders != null ? r.Borders.GetBorder(borderType) : null);
                            columnBorders = Enumerable.Distinct(Enumerable.Where(columnBorders, b => b != null));
                            return Enumerable.Concat(cellBorders, rowBorders, columnBorders);


                        }

                    case BorderType.InsideHorizontal:
                    case BorderType.InsideVertical:
                        return new Border[0];
                    case BorderType.All:
                    case BorderType.OutsideEdge:
                    throw new NotSupportedException();
            }
            throw new NotImplementedException();
        }

        internal override Cell FirstCell
        {
            get { return FirstRow[0]; }
        }


        internal override bool Contains(CellAddress address)
        {
            if (address.Row < FirstDimension.Index)
                return false;
            if (address.Row > LastDimension.Index)
                return false;
            return true;
        }
        internal bool Contains(Row row)
        {
            if (row.Worksheet != FirstRow.Worksheet)
                return false;
            return row.Index >= FirstRow.Index && row.Index <= LastRow.Index;
        }


        public override int ColumnSpan
        {
            get { return Spreadsheet.Column.MaxSpan; }
        }

        internal bool Intersects(FullRowRange r)
        {
            bool b1 = FirstRow.Index <= r.FirstRow.Index && r.FirstRow.Index <= LastRow.Index;
            bool b2 = r.FirstRow.Index <= FirstRow.Index && FirstRow.Index <= r.LastRow.Index;
            return b1 || b2;
        }


        public override bool IsFullRow
        {
            get { return true; }
        }

        public override bool IsFullColumn
        {
            get { return false; }
        }

        internal override RangeAddress Address
        {
            get { return new RangeAddress(new CellAddress(FirstRow.Index, 0), new CellAddress(LastRow.Index, Spreadsheet.Column.MaxIndex)); }
        }

        public override Cell this[int row, int column]
        {
            get { return Worksheet[FirstRow.Index + row, column]; }
        }

        internal override Cell GetNextCell(Cell cell, CellNavigationDirection direction)
        {
            switch (direction)
            {
                case CellNavigationDirection.Forward:

                    if (cell.Column.Index == ColumnSpan)
                    {
                        if (cell.Row.Index == FirstRow.Index + RowSpan - 1)
                            return Worksheet[FirstRow.Index, 0];
                        return Worksheet[FirstRow.Index, cell.Column.Index + 1];
                    }
                    return Worksheet[cell.Row.Index, cell.Column.Index + 1];
                case CellNavigationDirection.Backward:
                    if (cell.Column.Index == 0)
                    {
                        if (cell.Row.Index == FirstRow.Index)
                            return Worksheet[FirstRow.Index + RowSpan - 1, 0];
                        return Worksheet[FirstRow.Index, cell.Column.Index - 1];
                    }
                    return Worksheet[cell.Row.Index, cell.Column.Index - 1];
                default:
                    throw new NotSupportedException();
            }
        }

        internal override Range GetColumnRange(int index, RangeType type)
        {
            return new MultiCellsRange(Worksheet, new RangeAddress(new CellAddress(FirstRow.Index, index), new CellAddress(LastRow.Index, index)));
        }
        internal override Range GetRowRange(int index, RangeType type)
        {
            return new FullRowRange(FirstRow.Worksheet.Rows[FirstRow.Index + index]);
        }


        public override IEnumerable<Cell> GetAllCells()
        {
            for (int row = FirstRow.Index; row <= LastRow.Index; row++)
            {
                for (int column = 0; column <= Spreadsheet.Column.MaxIndex; column++)
                {
                    yield return Worksheet[row, column];
                }
            }
        }


        internal override void SetCellValueNoUndo(CellProperty p, object value)
        {
            SetCellValueNoUndoForSelf(p, value);
            foreach (Column column in Worksheet.Columns)
                if (!Equals(column.GetCellValue(p), value))
                {
                    foreach (Row row in GetRows())
                    {
                        var cell = Worksheet.GetNonVolatileCell(row.Index, column.Index);
                        if (cell != null)
                            cell.SetCellValue(p, value);
                    }
                }
        }

        public override IEnumerable<Row> GetRows()
        {
            for (int i = FirstRow.Index; i <= LastRow.Index; i++)
                yield return Worksheet.Rows[i];
        }
        public override IEnumerable<Column> GetColumns()
        {
            foreach (Column column in Worksheet.Columns)
                yield return column;
        }

        internal override Range Offseted(CellRelativeAddress address)
        {
            return new FullRowRange(FirstRow + address.Row, LastRow + address.Row);
        }

    }
}
